<?php

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

namespace App\Services\System;

use App\Exceptions\AdminException;
use App\Model\Admin\SystemMenusModel;
use App\Services\BaseServices;
use App\Services\System\Admin\SystemRoleServices;
use App\Utility\Arr;
use EasySwoole\Component\Singleton;
use EasySwoole\ORM\Collection\Collection;
use EasySwoole\ORM\DbManager;
use FormBuilder\Exception\FormBuilderException;
use FormBuilder\Factory\Iview as Form;
use function create_form;
use function get_tree_children;
use function get_tree_value;
use function sort_list_tier;

/**
 * Description of SystemMenusServices
 *
 * @author zike
 */
class SystemMenusServices extends BaseServices {

    use Singleton;

    /**
     * 获取添加身份规格
     * @param $roles
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getMenus($roles): array {
	$field = ['menu_name', 'pid', 'id'];
	if (!$roles) {
	    $menus = $this->getMenusRoule([], ['is_del' => 0], $field);
	} else {
	    /** @var SystemRoleServices $service */
	    $rules = SystemRoleServices::getInstance()->getRoleRules(['status' => 1, 'id' => [$roles, 'IN']]);
	    $rulesArr = Arr::unique($rules); //
	    $menus = $this->getMenusRoule($rulesArr, ['is_del' => 0], $field); //whereIn('id', $value)->where('is_del', 0)->whereOr('pid', 0);
	}
	return $this->tidyMenuTier(false, $menus);
    }

    /**
     * 组合菜单数据
     * @param bool $adminFilter
     * @param $menusList
     * @param int $pid
     * @param array $navList
     * @return array
     */
    public function tidyMenuTier(bool $adminFilter = false, $menusList, int $pid = 0, array $navList = []): array {
	foreach ($menusList as $k => $menu) {
	    $menu['title'] = $menu['menu_name'];
	    unset($menu['menu_name']);
	    if ($menu['pid'] == $pid) {
		unset($menusList[$k]);
		$menu['children'] = $this->tidyMenuTier($adminFilter, $menusList, $menu['id']);
		if ($pid == 0 && !count($menu['children']))
		    continue;
		if ($menu['children'])
		    $menu['expand'] = true;
		$navList[] = $menu;
	    }
	}
	return $navList;
    }

    /**
     * 获取后台菜单树型结构列表
     * @param array $where
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getList(array $where) {//'menu_name|id|pid', "%$value%"
	$menusList = DbManager::getInstance()->invoke(function ($client) use ($where) {
	    $model = SystemMenusModel::invoke($client)->where('is_del', 0);
	    if ($where['is_show'] !== '')
		$model->where('is_show', $where['is_show']);
	    if ($where['keyword'] !== '') {
		$like = '%' . $where['keyword'] . '%';
		$model->where("(menu_name like ? OR id like ? OR pid like ?)", [$like, $like, $like]);
	    }
	    $ret = $model->order('sort', 'DESC')->order('id', 'ASC')->all();
	    return (new Collection($ret))->toArray();
	});
	return get_tree_children($menusList);
    }

    /**
     * 新增权限表单
     * @return array
     * @throws FormBuilderException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function createMenus() {
	return create_form('添加权限', $this->createMenusForm(), $this->url('/setting/save'));
    }

    /**
     * 修改权限菜单
     * @param int $id
     * @return array
     * @throws FormBuilderException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function updateMenus(int $id) {
	$menusInfo = SystemMenusModel::create()->get($id);
	if (!$menusInfo) {
	    throw new AdminException('数据不存在');
	}
	return create_form('修改权限', $this->createMenusForm($menusInfo->toArray()), $this->url('/setting/update/' . $id), 'PUT');
    }

    /**
     * 创建权限规格生表单
     * @param array $formData
     * @return mixed
     * @throws FormBuilderException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function createMenusForm(array $formData = []) {
	$field[] = Form::input('menu_name', '按钮名称', $formData['menu_name'] ?? '')->required('按钮名称必填');
	$field[] = Form::select('pid', '父级id', $formData['pid'] ?? 0)->setOptions($this->getFormSelectMenus())->filterable(1);
	$field[] = Form::input('menu_path', '路由名称', $formData['menu_path'] ?? '')->placeholder('请输入前台跳转路由地址')->required('请填写前台路由地址');
	$field[] = Form::input('unique_auth', '权限标识', $formData['unique_auth'] ?? '')->placeholder('不填写则后台自动生成');
	$params = $formData['params'] ?? '';
	$field[] = Form::input('params', '参数', is_array($params) ? '' : $params)->placeholder('举例:a/123/b/234');
	$field[] = Form::frameInput('icon', '图标', $this->url('admin/widget.widgets/icon', ['fodder' => 'icon']), $formData['icon'] ?? '')->icon('md-add')->height('500px');
	$field[] = Form::number('sort', '排序', (int) ($formData['sort'] ?? 0));
	$field[] = Form::radio('auth_type', '类型', $formData['auth_type'] ?? 1)->options([['value' => 2, 'label' => '接口'], ['value' => 1, 'label' => '菜单(菜单只显示三级)']]);
	$field[] = Form::radio('is_show', '状态', $formData['is_show'] ?? 1)->options([['value' => 0, 'label' => '关闭'], ['value' => 1, 'label' => '开启']]);
	$field[] = Form::radio('is_show_path', '是否为前端隐藏菜单', $formData['is_show_path'] ?? 0)->options([['value' => 1, 'label' => '是'], ['value' => 0, 'label' => '否']]);
	[$menuList, $data] = $this->getFormCascaderMenus((int) ($formData['pid'] ?? 0));
	$field[] = Form::cascader('menu_list', '父级id', $data)->data($menuList)->filterable(true);
	return $field;
    }

    /**
     * 获取form表单所需要的所要的菜单列表
     * @return array[]
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    protected function getFormSelectMenus() {
	$menuList = $this->getMenusRoule([], ['is_del' => 0], ['id', 'pid', 'menu_name']);
	$list = sort_list_tier($menuList, '0', 'pid', 'id');
	$menus = [['value' => 0, 'label' => '顶级按钮']];
	foreach ($list as $menu) {
	    $menus[] = ['value' => $menu['id'], 'label' => $menu['html'] . $menu['menu_name']];
	}
	return $menus;
    }

    /**
     * 获取form表单所需要的所要的菜单瀑布
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    protected function getFormCascaderMenus(int $value = 0) {
	$menuList = $this->getMenusRoule([], ['is_del' => 0], ['`id` as value', 'pid', '`menu_name` as label']);
	if ($value) {
	    $data = get_tree_value($menuList, $value);
	} else {
	    $data = [];
	}
	return [get_tree_children($menuList, 'children', 'value'), array_reverse($data)];
    }

    /**
     * 获取用户权限
     * @param array $rules
     * @param int $type
     * @return array
     */
    public function getRoleAuths(array $rules, int $type = 1) {
	if (empty($rules))
	    return [];
	//invoke方式，让ORM查询结束后马上归还资源，可以提高资源的利用率
	$auths = DbManager::getInstance()->invoke(function ($client) use ($rules, $type) {
	    $ret = SystemMenusModel::invoke($client)->where('auth_type', $type)->where('id', $rules, 'IN')->field(['api_url', 'methods'])->all();
	    return (new Collection($ret))->toArray();
	});
	return $auths;
    }

    /**
     * 获取后台权限菜单和权限
     * @param $rouleId
     * @param int $level
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getMenusList($rouleId, int $level) {
	/** @var SystemRoleServices $systemRoleServices */
	$rules = SystemRoleServices::getInstance()->getRoleRules(['status' => 1, 'id' => $rouleId]);
	$rulesStr = Arr::unique($rules);
	$menusList = $this->getMenusRoule(($level ? $rulesStr : []), ['auth_type' => 1, 'is_show' => 1, 'is_del' => 0]);
	$unique = $this->getMenusUnique(($level ? $rulesStr : []));
	return [Arr::getMenuIviewList($menusList), $unique];
    }

    /**
     * 获取权限菜单列表
     * @param array $where
     * @param array $field
     * @return \think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getMenusRoule(array $rules, array $where = [], ?array $field = []) {
	if (!$field) {
	    $field = ['id', 'menu_name', 'icon', 'pid', 'sort', 'menu_path', 'is_show', 'header', 'is_header', 'is_show_path'];
	}

	$auths = DbManager::getInstance()->invoke(function ($client) use ($rules, $where, $field) {
	    $ret = SystemMenusModel::invoke($client);
	    if ($where)
		$ret->where($where);
	    if ($rules)
		$ret->where('id', $rules, 'IN');
	    $ret = $ret->field($field)->order('sort', 'DESC')->order('id', 'DESC')->all();
	    return (new Collection($ret))->toArray(null, false);
	});
	return $auths ?? [];
    }

    /**
     * 获取菜单中的唯一权限
     * @param array $where
     * @return array
     */
    public function getMenusUnique(array $rules) {
	$auths = DbManager::getInstance()->invoke(function ($client) use ($rules) {
	    $ret = SystemMenusModel::invoke($client)->where('is_del', 0)->where('unique_auth', '', '<>');
	    if ($rules)
		$ret->where('id', $rules, 'IN');
	    return $ret->column('unique_auth');
	});
	return $auths ?? [];
    }

    /**
     * 获取菜单中的名称
     * @param array $where
     * @return array
     */
    public function getMenusNames(array $rules) {
	$auths = DbManager::getInstance()->invoke(function ($client) use ($rules) {
	    $ret = SystemMenusModel::invoke($client)->where('is_del', 0)->where('id', $rules, 'IN');
	    return $ret->column('menu_name');
	});
	return $auths ?? [];
    }

    /**
     * 获取菜单
     * @return array
     */
    public function getMenusTreeList() {
	return DbManager::getInstance()->invoke(function ($client) {
		    $menus = SystemMenusModel::invoke($client)
				    ->where(['is_show' => 1, 'auth_type' => 1, 'is_del' => 0, 'is_show_path' => 0])
				    ->field(['id', 'pid', 'menu_name', 'menu_path', 'unique_auth', 'sort'])
				    ->order('sort', 'DESC')->all();
		    $list = (new Collection($menus))->toArray();
		    $counts = SystemMenusModel::invoke($client)->where(['is_show' => 1, 'auth_type' => 1, 'is_del' => 0, 'is_show_path' => 0])->column('pid');
		    foreach ($list as &$item) {
			if (in_array($item['id'], $counts)) {
			    $item['type'] = 1;
			} else {
			    $item['type'] = 0;
			}
		    }
		    return $list;
		});
    }

}
